658371
@@ -20,6 +20,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.Set;
@@ -27,6 +28,8 @@
 import org.apache.commons.compress.archivers.ArchiveEntry;
 import org.apache.commons.compress.archivers.ArchiveInputStream;
 import org.apache.commons.compress.archivers.ArchiveOutputStream;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
 import org.apache.commons.compress.utils.IOUtils;
 
 /**
@@ -66,6 +69,47 @@
public ChangeSetPerformer(final ChangeSet changeSet) {
      */
     public ChangeSetResults perform(ArchiveInputStream in, ArchiveOutputStream out)
             throws IOException {
+        return perform(new ArchiveInputStreamIterator(in), out);
+    }
+
+    /**
+     * Performs all changes collected in this ChangeSet on the ZipFile and
+     * streams the result to the output stream. Perform may be called more than once.
+     * 
+     * This method finishes the stream, no other entries should be added
+     * after that.
+     * 
+     * @param in
+     *            the ZipFile to perform the changes on
+     * @param out
+     *            the resulting OutputStream with all modifications
+     * @throws IOException
+     *             if an read/write error occurs
+     * @return the results of this operation
+     */
+    public ChangeSetResults perform(ZipFile in, ArchiveOutputStream out)
+            throws IOException {
+        return perform(new ZipFileIterator(in), out);
+    }
+
+    /**
+     * Performs all changes collected in this ChangeSet on the input entries and
+     * streams the result to the output stream.
+     * 
+     * This method finishes the stream, no other entries should be added
+     * after that.
+     * 
+     * @param entryIterator
+     *            the entries to perform the changes on
+     * @param out
+     *            the resulting OutputStream with all modifications
+     * @throws IOException
+     *             if an read/write error occurs
+     * @return the results of this operation
+     */
+    private ChangeSetResults perform(ArchiveEntryIterator entryIterator,
+                                     ArchiveOutputStream out)
+            throws IOException {
         ChangeSetResults results = new ChangeSetResults();
 
         Set<Change> workingSet = new LinkedHashSet<Change>(changes);
@@ -80,8 +124,8 @@
public ChangeSetResults perform(ArchiveInputStream in, ArchiveOutputStream out)
             }
         }
 
-        ArchiveEntry entry = null;
-        while ((entry = in.getNextEntry()) != null) {
+        while (entryIterator.hasNext()) {
+            ArchiveEntry entry = entryIterator.next();
             boolean copy = true;
 
             for (Iterator<Change> it = workingSet.iterator(); it.hasNext();) {
@@ -109,7 +153,7 @@
public ChangeSetResults perform(ArchiveInputStream in, ArchiveOutputStream out)
             if (copy
                 && !isDeletedLater(workingSet, entry)
                 && !results.hasBeenAdded(entry.getName())) {
-                copyStream(in, out, entry);
+                copyStream(entryIterator.getInputStream(), out, entry);
                 results.addedFromStream(entry.getName());
             }
         }
@@ -176,4 +220,57 @@
private void copyStream(InputStream in, ArchiveOutputStream out,
         IOUtils.copy(in, out);
         out.closeArchiveEntry();
     }
+
+    /**
+     * Used in perform to abstract out getting entries and streams for
+     * those entries.
+     *
+     * <p>Iterator#hasNext is not allowed to throw exceptions that's
+     * why we can't use Iterator&lt;ArchiveEntry&gt; directly -
+     * otherwise we'd need to convert exceptions thrown in
+     * ArchiveInputStream#getNextEntry.</p>
+     */
+    interface ArchiveEntryIterator {
+        boolean hasNext() throws IOException;
+        ArchiveEntry next();
+        InputStream getInputStream() throws IOException;
+    }
+
+    private static class ArchiveInputStreamIterator
+        implements ArchiveEntryIterator {
+        private final ArchiveInputStream in;
+        private ArchiveEntry next;
+        ArchiveInputStreamIterator(ArchiveInputStream in) {
+            this.in = in;
+        }
+        public boolean hasNext() throws IOException {
+            return (next = in.getNextEntry()) != null;
+        }
+        public ArchiveEntry next() {
+            return next;
+        }
+        public InputStream getInputStream() {
+            return in;
+        }
+    }
+
+    private static class ZipFileIterator
+        implements ArchiveEntryIterator {
+        private final ZipFile in;
+        private final Enumeration<ZipArchiveEntry> nestedEnum;
+        private ZipArchiveEntry current;
+        ZipFileIterator(ZipFile in) throws IOException {
+            this.in = in;
+            nestedEnum = in.getEntriesInPhysicalOrder();
+        }
+        public boolean hasNext() {
+            return nestedEnum.hasMoreElements();
+        }
+        public ArchiveEntry next() {
+            return (current = nestedEnum.nextElement());
+        }
+        public InputStream getInputStream() throws IOException {
+            return in.getInputStream(current);
+        }
+    }
 }
